home *** CD-ROM | disk | FTP | other *** search
- /* Copyright (c) 1990,1991,1992 Chris and John Downey */
- #ifndef lint
- static char *sccsid = "@(#)yankput.c 2.4 (Chris & John Downey) 8/6/92";
- #endif
-
- /***
-
- * program name:
- xvi
- * function:
- PD version of UNIX "vi" editor, with extensions.
- * module name:
- yankput.c
- * module function:
- Functions to handle "yank" and "put" commands.
-
- Note that there is still some code in normal.c to do
- some of the work - this will have to be changed later.
-
- Some of the routines and data structures herein assume ASCII
- order, so I don't know if they are particularly portable.
- * history:
- STEVIE - ST Editor for VI Enthusiasts, Version 3.10
- Originally by Tim Thompson (twitch!tjt)
- Extensive modifications by Tony Andrews (onecom!wldrdg!tony)
- Heavily modified by Chris & John Downey
-
- ***/
-
- #include "xvi.h"
-
- /*
- * Structure to store yanked text or yanked lines.
- */
- typedef struct yankbuffer {
- enum {
- y_none,
- y_chars,
- y_lines
- } y_type;
-
- char *y_1st_text;
- char *y_2nd_text;
- Line *y_line_buf;
- } Yankbuffer;
-
- /*
- * For named buffers, we have an array of yankbuffer structures,
- * mapped by printable ascii characters. Only the alphabetic
- * characters, and '@', are directly settable by the user.
- * The uppercase versions mean "append" rather than "replace".
- */
- #define LOWEST_NAME ' '
- #define HIGHEST_NAME 'Z'
- #define NBUFS (HIGHEST_NAME - LOWEST_NAME + 1)
- #define bufno(c) ((c) - LOWEST_NAME)
- #define validname(c) ((c) >= LOWEST_NAME && (c) < 'A')
-
- static Yankbuffer yb[NBUFS];
-
- static void put P((char *, bool_t, bool_t));
- static Yankbuffer *yp_get_buffer P((int));
- static Line *copy_lines P((Line *, Line *));
- static char *yanktext P((Posn *, Posn *));
- static void yp_free P((Yankbuffer *));
-
- void
- init_yankput()
- {
- }
-
- /*
- * Set the buffer name to be used for the next yank/put operation to
- * the given character. The character '@' is used as a synonym for the
- * default (unnamed) buffer.
- */
- static Yankbuffer *
- yp_get_buffer(name)
- int name;
- {
- int i;
-
- if (validname(name)) {
- i = bufno(name);
- } else if (is_alpha(name)) {
- if (is_upper(name)) {
- show_message(curwin, "Appending to named buffers not supported");
- }
- i = bufno(to_upper(name));
- } else {
- show_error(curwin, "Illegal buffer name");
- return(NULL);
- }
- return(&yb[i]);
- }
-
- /*
- * Yank the text specified by the given start/end positions.
- * The fourth parameter is TRUE if we are doing a character-
- * based, rather than a line-based, yank.
- *
- * For line-based yanks, the range of positions is inclusive.
- *
- * Returns TRUE if successfully yanked.
- *
- * Positions must be ordered properly, i.e. "from" <= "to".
- */
- /*ARGSUSED*/
- bool_t
- do_yank(buffer, from, to, charbased, name)
- Buffer *buffer;
- Posn *from, *to;
- bool_t charbased;
- int name;
- {
- Yankbuffer *yp_buf;
- long nlines;
-
- yp_buf = yp_get_buffer(name);
- if (yp_buf == NULL) {
- return(FALSE);
- }
- yp_free(yp_buf);
-
- nlines = cntllines(from->p_line, to->p_line);
-
- if (charbased) {
- Posn ptmp;
-
- /*
- * First yank either the whole of the text string
- * specified (if from and to are on the same line),
- * or from "from" to the end of the line.
- */
- ptmp.p_line = from->p_line;
- if (to->p_line == from->p_line) {
- ptmp.p_index = to->p_index;
- } else {
- ptmp.p_index = strlen(from->p_line->l_text) - 1;
- }
- yp_buf->y_1st_text = yanktext(from, &ptmp);
- if (yp_buf->y_1st_text == NULL) {
- return(FALSE);
- }
-
- /*
- * Next, determine if it is a multi-line character-based
- * yank, in which case we have to yank from the start of
- * the line containing "to" up to "to" itself.
- */
- if (nlines > 1) {
- ptmp.p_line = to->p_line;
- ptmp.p_index = 0;
- yp_buf->y_2nd_text = yanktext(&ptmp, to);
- if (yp_buf->y_1st_text == NULL) {
- free(yp_buf->y_1st_text);
- return(FALSE);
- }
- }
-
- /*
- * Finally, we may need to yank any lines between "from"
- * and "to".
- */
- if (nlines > 2) {
- yp_buf->y_line_buf =
- copy_lines(from->p_line->l_next, to->p_line);
- if (yp_buf->y_line_buf == NULL) {
- free(yp_buf->y_1st_text);
- free(yp_buf->y_2nd_text);
- return(FALSE);
- }
- }
-
- yp_buf->y_type = y_chars;
- } else {
- /*
- * Yank lines starting at "from", ending at "to".
- */
- yp_buf->y_line_buf = copy_lines(from->p_line,
- to->p_line->l_next);
- if (yp_buf->y_line_buf == NULL) {
- return(FALSE);
- }
- yp_buf->y_type = y_lines;
- }
- return(TRUE);
- }
-
- /*
- * Yank the given string.
- *
- * Third parameter indicates whether to do it as a line or a string.
- *
- * Returns TRUE if successfully yanked.
- */
- bool_t
- yank_str(name, str, line_based)
- int name;
- char *str;
- bool_t line_based;
- {
- Yankbuffer *yp_buf;
- register Line *tmp;
- register char *cp;
-
- yp_buf = yp_get_buffer(name);
- if (yp_buf == NULL) {
- return(FALSE);
- }
- yp_free(yp_buf);
-
- /*
- * Obtain space to store the string.
- */
- if (line_based) {
- /*
- * First try to save the string. If no can do,
- * return FALSE without affecting the current
- * contents of the yank buffer.
- */
- tmp = newline(strlen(str) + 1);
- if (tmp == NULL) {
- return(FALSE);
- }
- tmp->l_prev = tmp->l_next = NULL;
- (void) strcpy(tmp->l_text, str);
- } else {
- cp = strsave(str);
- if (cp == NULL) {
- return(FALSE);
- }
- }
-
- /*
- * Set up the yank structure.
- */
- if (line_based) {
- yp_buf->y_type = y_lines;
- yp_buf->y_line_buf = tmp;
- } else {
- yp_buf->y_type = y_chars;
- yp_buf->y_1st_text = cp;
- }
-
- return(TRUE);
- }
-
- /*
- * Put back the last yank at the specified position,
- * in the specified direction.
- */
- void
- do_put(win, location, direction, name)
- Xviwin *win;
- Posn *location;
- int direction;
- int name;
- {
- Yankbuffer *yp_buf;
- register Line *currline; /* line we are on now */
- register Line *nextline; /* line after currline */
- Buffer *buffer;
-
- yp_buf = yp_get_buffer(name);
- if (yp_buf == NULL) {
- return;
- }
-
- buffer = win->w_buffer;
-
- /*
- * Set up current and next line pointers.
- */
- currline = location->p_line;
- nextline = currline->l_next;
-
- /*
- * See which type of yank it was ...
- */
- if (yp_buf->y_type == y_chars) {
- int l;
-
- l = win->w_cursor->p_index;
- if (direction == FORWARD && currline->l_text[l] != '\0') {
- ++l;
- }
-
- if (!start_command(win)) {
- return;
- }
-
- /*
- * Firstly, insert the 1st_text buffer, since this is
- * always present. We may wish to split the line after
- * the inserted text if this was a multi-line yank.
- */
- replchars(win, currline, l, 0, yp_buf->y_1st_text);
- updateline(win);
-
- if (yp_buf->y_2nd_text != NULL) {
- int end_of_1st_text;
- Line *newl;
-
- end_of_1st_text = l + strlen(yp_buf->y_1st_text);
- newl = newline(strlen(yp_buf->y_1st_text) + SLOP);
- if (newl == NULL)
- return;
-
- /*
- * Link the new line into the list.
- */
- repllines(win, nextline, 0L, newl);
- nextline = newl;
- replchars(win, nextline, 0, 0,
- currline->l_text + end_of_1st_text);
- replchars(win, currline, end_of_1st_text,
- strlen(currline->l_text + end_of_1st_text), "");
-
- }
-
- if (yp_buf->y_line_buf != NULL) {
- Line *newlines;
-
- newlines = copy_lines(yp_buf->y_line_buf, (Line *) NULL);
- if (newlines != NULL) {
- repllines(win, nextline, 0L, newlines);
- }
- }
-
- if (yp_buf->y_2nd_text != NULL) {
- if (nextline == buffer->b_lastline) {
- Line *new;
-
- /*
- * Can't put the remainder of the text
- * on the following line, 'cos there
- * isn't one, so we have to create a
- * new line.
- */
- new = newline(strlen(yp_buf->y_2nd_text) + 1);
- if (new == NULL) {
- end_command(win);
- return;
- }
- repllines(win, nextline, 0L, new);
- nextline = new;
- }
- replchars(win, nextline, 0, 0, yp_buf->y_2nd_text);
- }
-
- end_command(win);
-
- /*
- * Move on to the last character of the inserted text.
- */
- if (direction == BACKWARD) {
- (void) one_left(curwin, FALSE);
- }
-
- cursupdate(win);
- update_buffer(buffer);
-
- } else if (yp_buf->y_type == y_lines) {
-
- Line *new; /* first line of lines to be put */
-
- /*
- * Make a new copy of the saved lines.
- */
- new = copy_lines(yp_buf->y_line_buf, (Line *) NULL);
- if (new == NULL) {
- return;
- }
-
- repllines(win, (direction == FORWARD) ? nextline : currline, 0L, new);
-
- /*
- * Put the cursor at the "right" place
- * (i.e. the place the "real" vi uses).
- */
- move_cursor(win, new, 0);
- begin_line(win, TRUE);
- move_window_to_cursor(win);
- cursupdate(win);
- update_buffer(buffer);
- } else {
- show_error(win, "Nothing to put!");
- }
- }
-
- /*
- * Stuff the specified buffer into the input stream.
- * Called by the '@' command.
- *
- * The "vi_mode" parameter will be FALSE if the buffer should
- * be preceded by a ':' and followed by a '\n', i.e. it is the
- * result of a :@ command rather than a vi-mode @ command.
- */
- void
- yp_stuff_input(win, name, vi_mode)
- Xviwin *win;
- int name;
- bool_t vi_mode;
- {
- Yankbuffer *yp_buf;
-
- yp_buf = yp_get_buffer(name);
- if (yp_buf == NULL) {
- show_error(win, "Nothing in buffer %c", name);
- return;
- }
-
- switch (yp_buf->y_type) {
- case y_chars:
- put(yp_buf->y_1st_text, vi_mode, FALSE);
- break;
-
- case y_lines:
- break;
-
- default:
- show_error(win, "Nothing to put!");
- return;
- }
-
- if (yp_buf->y_line_buf != NULL) {
- Line *lp;
-
- for (lp = yp_buf->y_line_buf; lp != NULL; lp = lp->l_next) {
- put(lp->l_text, vi_mode, TRUE);
- }
- }
-
- if (yp_buf->y_type == y_chars && yp_buf->y_2nd_text != NULL) {
- put(yp_buf->y_2nd_text, vi_mode, FALSE);
- }
- }
-
- static void
- put(str, vi_mode, newline)
- char *str;
- bool_t vi_mode;
- bool_t newline;
- {
- stuff("%s%s%s",
- (!vi_mode && str[0] != ':') ? ":" : "",
- str,
- (!vi_mode || newline) ? "\n" : "");
- }
-
- /*
- * Copy the lines pointed at by "from", up to but not including
- * pointer "to" (which might be NULL), into new memory and return
- * a pointer to the start of the new list.
- *
- * Returns NULL for errors.
- */
- static Line *
- copy_lines(from, to)
- Line *from, *to;
- {
- Line *src;
- Line head;
- Line *dest = &head;
-
- for (src = from; src != to; src = src->l_next) {
- Line *tmp;
-
- tmp = newline(strlen(src->l_text) + 1);
- if (tmp == NULL) {
- throw(head.l_next);
- return(NULL);
- }
-
- /*
- * Copy the line's text over, and advance
- * "dest" to point to the new line structure.
- */
- (void) strcpy(tmp->l_text, src->l_text);
- tmp->l_next = NULL;
- tmp->l_prev = dest;
- dest->l_next = tmp;
- dest = tmp;
- }
-
- return(head.l_next);
- }
-
- static char *
- yanktext(from, to)
- Posn *from, *to;
- {
- int nchars;
- char *cp;
-
- nchars = to->p_index - from->p_index + 1;
- cp = (char *) alloc((unsigned) nchars + 1);
- if (cp == NULL) {
- return(NULL);
- }
-
- (void) strncpy(cp, from->p_line->l_text + from->p_index, nchars);
- cp[nchars] = '\0';
-
- return(cp);
- }
-
- static void
- yp_free(yp)
- Yankbuffer *yp;
- {
- if (yp->y_type == y_lines) {
- throw(yp->y_line_buf);
- yp->y_line_buf = NULL;
- } else if (yp->y_type == y_chars) {
- free(yp->y_1st_text);
- yp->y_1st_text = NULL;
- if (yp->y_2nd_text != NULL)
- free(yp->y_2nd_text);
- yp->y_2nd_text = NULL;
- if (yp->y_line_buf != NULL)
- throw(yp->y_line_buf);
- yp->y_line_buf = NULL;
- }
- yp->y_type = y_none;
- }
-
- /*
- * Push up buffers 1..8 by one, spilling 9 off the top.
- * Then move '@' into '1'.
- *
- * This routine assumes contiguity of characters '0' to '9',
- * i.e. probably ASCII, but what the hell.
- */
- void
- yp_push_deleted()
- {
- Yankbuffer *atp;
- int c;
-
- yp_free(&yb[bufno('9')]);
- for (c = '9'; c > '1'; --c) {
- yb[bufno(c)] = yb[bufno(c - 1)];
- }
- atp = &yb[bufno('@')];
- yb[bufno('1')] = *atp;
- atp->y_type = y_none;
- atp->y_line_buf = NULL;
- atp->y_1st_text = NULL;
- atp->y_2nd_text = NULL;
- }
-